// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#include "rotor-light.hpp"

#ifdef __x86_64__
#define __host_comilation__ 1
#else
#ifdef __linux__
#define __host_comilation__ 1
#endif
#endif

#ifdef __AVR__

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <util/delay.h>

#elif __host_comilation__

#include <cstdlib>

#elif __arm__

#endif

namespace rl = rotor_light;

namespace message {
struct Ping : rl::Message {
  using Message::Message;
  static constexpr auto type_id = __LINE__;
  rl::MessageTypeId get_type_id() const override { return type_id; }
};

struct Pong : rl::Message {
  using Message::Message;
  static constexpr auto type_id = __LINE__;
  rl::MessageTypeId get_type_id() const override { return type_id; }
};
} // namespace message

struct Pinger : rl::Actor<2> {
  using Parent = Actor<2>;

  void initialize() override {
    subscribe(&Pinger::on_pong);
    Parent::initialize();
  }

  void advance_start() override {
    Parent::advance_start();
    ping();
  }

  void ping() {
    --left;
    send<message::Ping>(0, ponger_id);
  }

  void on_pong(message::Pong &) {
    if (left > 0) {
      ping();
    }
  }

  uint32_t left;
  rl::ActorId ponger_id;
};

struct Ponger : rl::Actor<2> {
  using Parent = Actor<2>;

  void initialize() override {
    subscribe(&Ponger::on_ping);
    Parent::initialize();
  }
  void on_ping(message::Ping &) { send<message::Pong>(0, pinger_id); }
  rl::ActorId pinger_id;
};

using Supervisor =
    rl::Supervisor<rl::SupervisorBase::min_handlers_amount, Pinger, Ponger>;
using Storage = rl::traits::MessageStorage<rl::message::ChangeState,
                                           rl::message::ChangeStateAck,
                                           message::Ping, message::Pong>;
using Queue = rl::Queue<Storage, 5>; /* upto 5 messages in 1 queue */

#ifdef __AVR__
#define LED PB5

constexpr uint32_t amount = 100000;

void on_start() {
  wdt_reset();
  MCUSR &= ~(1U << WDRF);
  WDTCSR |= (1U << WDCE) | (1U << WDE);
  WDTCSR = 0x00;
  PORTB &= ~(1U << PORTB5);
  DDRB |= (1 << LED);
  PORTB ^= (1 << LED);
  sei();
}

void on_finish() {
  PORTB ^= (1 << LED);
  _delay_ms(1000);
}
#elif __host_comilation__
constexpr uint32_t amount = 100000000;
void on_start() {}
void on_finish() { std::exit(0); }
#endif

/* allocate */
Queue queue;
rl::Context context{&queue, nullptr, nullptr};
Supervisor sup;

int main(int argc, char **argv) {
  while (true) {

    /* setup */
    sup.bind(context);
    auto pinger = sup.get_child<0>();
    auto ponger = sup.get_child<1>();
    pinger->left = amount;
    pinger->ponger_id = ponger->get_id();
    ponger->pinger_id = pinger->get_id();

    /* disable polling timer timer */
    sup.start(false);
    on_start();
    sup.process();
    on_finish();

    sup.stop();
    sup.process();
  }
}
